package com.gitee.wsl.android.ext.okio

import android.annotation.SuppressLint
import android.net.Uri
import android.provider.MediaStore
import com.gitee.wsl.android.ext.store.contentResolver
import com.gitee.wsl.ext.okio.isPhysicalFile
import com.gitee.wsl.jvm.ext.store.requireFileCreate
import okio.FileMetadata
import okio.FileNotFoundException
import okio.IOException
import okio.Path
import okio.Path.Companion.toPath
import okio.Sink
import okio.Source
import okio.sink
import okio.source


fun Path.toUri(): Uri {
    val str = this.toString()

    if (str.startsWith("content:/")) {
        return Uri.parse(str.replace("content:/", "content://"))
    }

    return Uri.parse(str)
}

fun Uri.toOkioPath(): Path {
    return this.toString().toPath(false)
}


@SuppressLint("Recycle")
fun Path.appendingSink(mustExist: Boolean = false): Sink {
    if (isPhysicalFile) {
        val target = toFile()
        if (mustExist) if (!target.exists()) throw IOException("$target doesn't exist.")

        return target.sink(append = true)
    }

    if (!mustExist) {
        throw IOException("Appending on an inexisting path isn't supported ($this)")
    }

    val uri = toUri()
    val outputStream = contentResolver.openOutputStream(uri, "a")

    if (outputStream == null) {
        throw IOException("Couldn't open an OutputStream ($this)")
    } else {
        return outputStream.sink()
    }
}

fun Path.list(throwOnFailure: Boolean = true): List<Path>? {
    if (isPhysicalFile) {
        return OkioExt.listPhysicalDirectory(this, throwOnFailure)
    }
    return OkioExt.listDocumentProvider(this, throwOnFailure)
}

fun Path.metadataOrNull(): FileMetadata? {
    val uri = toUri()

    return when (uri.authority) {
        null -> OkioExt.fetchMetadataFromPhysicalFile(this)
        MediaStore.AUTHORITY -> OkioExt.fetchMetadataFromMediaStore(this, uri)
        else -> OkioExt.fetchMetadataFromDocumentProvider(this, uri)
    }
}

fun Path.delete(mustExist: Boolean=false){
    if (isPhysicalFile) {
        val file = toFile()
        val deleted = file.delete()
        if (!deleted) {
            if (!file.exists()) throw FileNotFoundException("no such file: $this")
            else throw IOException("failed to delete $this")
        }
    } else {
        val uri = toUri()
        val deletedRows = contentResolver.delete(uri, null, null)

        if (deletedRows == 0) {
            throw IOException("failed to delete $this")
        }
    }
}

@SuppressLint("Recycle")
fun Path.sink(mustCreate: Boolean=false): Sink {
    if (isPhysicalFile) {
        val target = toFile()
        if (mustCreate) target.requireFileCreate()

        return toFile().sink()
    }

    if (mustCreate) {
        throw IOException("Path creation isn't supported ($this)")
    }

    val uri = toUri()
    val outputStream = contentResolver.openOutputStream(uri)

    if (outputStream == null) {
        throw IOException("Couldn't open an OutputStream ($this)")
    } else {
        return outputStream.sink()
    }
}

@SuppressLint("Recycle")
fun Path.source(): Source {
    if (isPhysicalFile) {
        return toFile().source()
    }

    val uri = toUri()
    val inputStream = contentResolver.openInputStream(uri)

    if (inputStream == null) {
        throw IOException("Couldn't open an InputStream ($this)")
    } else {
        return inputStream.source()
    }
}


